﻿Ext.ns('Ext.ux.tree');
Ext.ux.tree.CheckTreePanel = Ext.extend(Ext.tree.TreePanel, {
    bubbleCheck: 'checked',
    cascadeCheck: 'unchecked',
    deepestOnly: false,
    expandOnCheck: true,
    filter: true,
    separator: ',',
    cls: 'ux-checktree',
    baseAttrs: {},
    checkedNodes: [],
    initComponent: function() {
        Ext.ux.tree.CheckTreePanel.superclass.initComponent.apply(this, arguments);
        
        var baseAttrs = Ext.apply({
            uiProvider: Ext.ux.tree.CheckTreeNodeUI
        }, this.baseAttrs);
        Ext.applyIf(this.loader, {
            baseAttrs: baseAttrs
        });
        
        this.loader.on('load', function(loader, node) {
            if (this.checkedNodes.length > 0) {
                this.setValue(this.checkedNodes, true);
            }
        }, this);
        
        this.eventModel = new Ext.ux.tree.CheckTreeEventModel(this);
        
        if (true === this.filter) {
            var Filter = Ext.ux.tree.TreeFilterX ? Ext.ux.tree.TreeFilterX : Ext.tree.TreeFilter;
            this.filter = new Filter(this, {
                autoClear: true
            });
        }
        
    },
    getValue: function() {
        var a = [];
        this.root.cascade(function(n) {
            if (true === n.attributes.checked) {
                if (false === this.deepestOnly || !this.isChildChecked(n)) {
                    a.push(n.id);
                }
            }
        }, this);
        return a;
    },
    isChildChecked: function(node) {
        var checked = false;
        Ext.each(node.childNodes, function(child) {
            if (child.attributes.checked) {
                checked = true;
            }
        });
        return checked;
    },
    clearValue: function() {
        this.root.cascade(function(n) {
            var ui = n.getUI();
            if (ui && ui.setChecked) {
                ui.setChecked(false);
            }
        });
        this.value = [];
        this.getSelectionModel().clearSelections();
        return this;
    },
    convertValue: function(val) {
        // init return array
        var a = [];
        
        // calls itself recursively if necessary
        if (1 < arguments.length) {
            for (var i = 0; i < arguments.length; i++) {
                a.push(this.convertValue(arguments[i]));
            }
        } else if (Ext.isArray(val)) {
            a = val;
        } else if ('number' === typeof val) {
            a.push(val);
        } else if ('string' === typeof val) {
            a = val.split(this.separator);
        }
        
        return a;
    },
    setValue: function(val, isKeep) {
    
        if (isKeep !== true) {
            // uncheck all first
            this.clearValue();
        }
        
        // process arguments
        this.value = this.convertValue.apply(this, val);
        
        // check nodes
        Ext.each(this.value, function(id) {
            var n = this.getNodeById(id);
            if (n) {
                var ui = n.getUI();
                if (ui && ui.setChecked) {
                    ui.setChecked(true);
                    
                    // expand checked nodes
                    if (true === this.expandOnCheck) {
                        n.bubbleExpand();
                    }
                }
            }
        }, this);
        
        return this.value;
    },
    serialize: function(attr) {
        attr = attr || 'text';
        var a = [];
        this.root.cascade(function(n) {
            if (true === n.attributes.checked) {
                if (false === this.deepestOnly || !this.isChildChecked(n)) {
                    a.push(n[attr]);
                }
            }
        }, this);
        return a.join(this.separator + ' ');
    },
    getText: function(attr) {
        return this.serialize(attr);
    }
});

Ext.reg('checktreepanel', Ext.ux.tree.CheckTreePanel);
Ext.override(Ext.tree.TreeNode, {
    bubbleExpand: function() {
        var root = this.getOwnerTree().root;
        var branch = [];
        var p = this;
        while (p !== root) {
            p = p.parentNode;
            branch.push(p);
        }
        branch.reverse();
        Ext.each(branch, function(n) {
            n.expand(false, false);
        });
    }
});

Ext.ux.tree.CheckTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
    renderElements: function(n, a, targetNode, bulkRender) {
    
        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
        var checked = n.attributes.checked;
        var href = a.href ? a.href : Ext.isGecko ? "" : "#";
        var buf = ['<li class="x-tree-node"><div ext:tree-node-id="', n.id, '" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls, '" unselectable="on">', '<span class="x-tree-node-indent">', this.indentMarkup, "</span>", '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />', '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon', (a.icon ? " x-tree-node-inline-icon" : ""), (a.iconCls ? " " + a.iconCls : ""), '" unselectable="on" />', '<img src="' + this.emptyIcon + '" class="x-tree-checkbox' + (true === checked ? ' x-tree-node-checked' : '') + '" />', '<a hidefocus="on" class="x-tree-node-anchor" href="', href, '" tabIndex="1" ', a.hrefTarget ? ' target="' + a.hrefTarget + '"' : "", '><span unselectable="on">', n.text, "</span></a></div>", '<ul class="x-tree-node-ct" style="display:none;"></ul>', "</li>"].join('');
        var nel;
        if (bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())) {
            this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
        } else {
            this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
        }
        this.elNode = this.wrap.childNodes[0];
        this.ctNode = this.wrap.childNodes[1];
        var cs = this.elNode.childNodes;
        this.indentNode = cs[0];
        this.ecNode = cs[1];
        this.iconNode = cs[2];
        this.checkbox = cs[3];
        this.cbEl = Ext.get(this.checkbox);
        this.anchor = cs[4];
        this.textNode = cs[4].firstChild;
    },
    setIconCls: function(iconCls) {
        Ext.fly(this.iconNode).set({
            cls: 'x-tree-node-icon ' + iconCls
        });
    },
    isChecked: function() {
        return this.node.attributes.checked === true;
    },
    onCheckChange: function() {
        var checked = this.isChecked();
        var tree = this.node.getOwnerTree();
        var bubble = tree.bubbleCheck;
        var cascade = tree.cascadeCheck;
        
        if ('all' === bubble || (checked && 'checked' === bubble) || (!checked && 'unchecked' === bubble)) {
            this.updateParent(checked);
        }
        if ('all' === cascade || (checked && 'checked' === cascade) || (!checked && 'unchecked' === cascade)) {
            this.updateChildren(checked);
        }
        
        this.fireEvent('checkchange', this.node, checked);
    },
    setChecked: function(checked) {
        checked = true === checked ? checked : false;
        var cb = this.cbEl || false;
        if (cb && checked) {
            cb.addClass('x-tree-node-checked');
        } else {
            cb.removeClass('x-tree-node-checked');
        }
        this.node.attributes.checked = checked;
        this.onCheckChange();
        return checked;
    },
    toggleCheck: function() {
        var checked = !this.isChecked();
        this.setChecked(checked);
        return checked;
    },
    updateParent: function(checked) {
        var p = this.node.parentNode;
        var ui = p ? p.getUI() : false;
        
        if (ui && ui.setChecked) {
            ui.setChecked(checked);
        }
    },
    updateChildren: function(checked) {
        this.node.eachChild(function(n) {
            var ui = n.getUI();
            if (ui && ui.setChecked) {
                ui.setChecked(checked);
            }
        });
    },
    onCheckboxClick: function() {
        if (!this.disabled) {
            this.toggleCheck();
        }
    },
    onCheckboxOver: function() {
        this.addClass('x-tree-checkbox-over');
    },
    onCheckboxOut: function() {
        this.removeClass('x-tree-checkbox-over');
    },
    onCheckboxDown: function() {
        this.addClass('x-tree-checkbox-down');
    },
    onCheckboxUp: function() {
        this.removeClass('x-tree-checkbox-down');
    }
});

Ext.ux.tree.CheckTreeEventModel = Ext.extend(Ext.tree.TreeEventModel, {
    initEvents: function() {
        var el = this.tree.getTreeEl();
        el.on('click', this.delegateClick, this);
        if (this.tree.trackMouseOver !== false) {
            el.on('mouseover', this.delegateOver, this);
            el.on('mouseout', this.delegateOut, this);
        }
        el.on('mousedown', this.delegateDown, this);
        el.on('mouseup', this.delegateUp, this);
        el.on('dblclick', this.delegateDblClick, this);
        el.on('contextmenu', this.delegateContextMenu, this);
    },
    delegateOver: function(e, t) {
        if (!this.beforeEvent(e)) {
            return;
        }
        if (this.lastEcOver) {
            this.onIconOut(e, this.lastEcOver);
            delete this.lastEcOver;
        }
        if (this.lastCbOver) {
            this.onCheckboxOut(e, this.lastCbOver);
            delete this.lastCbOver;
        }
        if (e.getTarget('.x-tree-ec-icon', 1)) {
            this.lastEcOver = this.getNode(e);
            this.onIconOver(e, this.lastEcOver);
        } else if (e.getTarget('.x-tree-checkbox', 1)) {
            this.lastCbOver = this.getNode(e);
            this.onCheckboxOver(e, this.lastCbOver);
        }
        t = this.getNodeTarget(e);
        if (t) {
            this.onNodeOver(e, this.getNode(e));
        }
    },
    delegateOut: function(e, t) {
        if (!this.beforeEvent(e)) {
            return;
        }
        var n;
        if (e.getTarget('.x-tree-ec-icon', 1)) {
            n = this.getNode(e);
            this.onIconOut(e, n);
            if (n == this.lastEcOver) {
                delete this.lastEcOver;
            }
        } else if (e.getTarget('.x-tree-checkbox', 1)) {
            n = this.getNode(e);
            this.onCheckboxOut(e, n);
            if (n == this.lastCbOver) {
                delete this.lastCbOver;
            }
        }
        if ((t = this.getNodeTarget(e)) && !e.within(t, true)) {
            this.onNodeOut(e, this.getNode(e));
        }
    },
    delegateDown: function(e, t) {
        if (!this.beforeEvent(e)) {
            return;
        }
        if (e.getTarget('.x-tree-checkbox', 1)) {
            this.onCheckboxDown(e, this.getNode(e));
        }
    },
    delegateUp: function(e, t) {
        if (!this.beforeEvent(e)) {
            return;
        }
        if (e.getTarget('.x-tree-checkbox', 1)) {
            this.onCheckboxUp(e, this.getNode(e));
        }
    },
    delegateOut: function(e, t) {
        if (!this.beforeEvent(e)) {
            return;
        }
        var n;
        if (e.getTarget('.x-tree-ec-icon', 1)) {
            n = this.getNode(e);
            this.onIconOut(e, n);
            if (n == this.lastEcOver) {
                delete this.lastEcOver;
            }
        } else if (e.getTarget('.x-tree-checkbox', 1)) {
            n = this.getNode(e);
            this.onCheckboxOut(e, n);
            if (n == this.lastCbOver) {
                delete this.lastCbOver;
            }
        }
        if ((t = this.getNodeTarget(e)) && !e.within(t, true)) {
            this.onNodeOut(e, this.getNode(e));
        }
    },
    delegateClick: function(e, t) {
        if (!this.beforeEvent(e)) {
            return;
        }
        if (e.getTarget('.x-tree-checkbox', 1)) {
            this.onCheckboxClick(e, this.getNode(e));
        } else if (e.getTarget('.x-tree-ec-icon', 1)) {
            this.onIconClick(e, this.getNode(e));
        } else if (this.getNodeTarget(e)) {
            this.onNodeClick(e, this.getNode(e));
        }
    },
    onCheckboxClick: function(e, node) {
        node.ui.onCheckboxClick();
    },
    onCheckboxOver: function(e, node) {
        node.ui.onCheckboxOver();
    },
    onCheckboxOut: function(e, node) {
        node.ui.onCheckboxOut();
    },
    onCheckboxDown: function(e, node) {
        node.ui.onCheckboxDown();
    },
    onCheckboxUp: function(e, node) {
        node.ui.onCheckboxUp();
    }
});
